package org.xlp.expression;

import org.xlp.assertion.AssertUtils;
import org.xlp.utils.XLPArrayUtil;
import org.xlp.utils.XLPStringUtil;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Stack;

/**
 * Description:表达式单元格解析器
 * <br/>date: 2024/12/28 23:20
 *
 * @version 1.0
 * @author: xlp
 */
public class ExpressionItemParser implements IExpressionItemParser{
    /**
     * 表达式
     */
    private String expression;

    /**
     * 是否是小数表达式
     */
    private boolean isDecimal = false;

    /**
     * 是否包含加减法
     */
    private boolean isAddOrSubtract = false;

    /**
     * 是否包含乘除法
     */
    private boolean isMultiplyOrDivide = false;

    /**
     * 是否抱包含括号
     */
    private boolean hasBracket = false;

    /**
     * 表达式项
     */
    private String[] items;

    /**
     * 标记是否是大整数表达式
     */
    private boolean isBigInteger = false;

    /**
     * 表达式大整数校验器
     */
    private BigIntegerChecker checker;

    /**
     * 表达式是否包含科学计算
     */
    private boolean hasE = false;

    /**
     * 构造函数
     * @param expression
     * @throws NullPointerException 假如参数为null或空，则抛出该异常
     */
    public ExpressionItemParser(String expression){
        this(expression, null);
    }

    /**
     * 构造函数
     * @param expression
     * @param checker 大整数校验器
     * @throws NullPointerException 假如第一个参数为null或空，则抛出该异常
     */
    public ExpressionItemParser(String expression, BigIntegerChecker checker){
        AssertUtils.isNotNull(expression, "expression parameter must be not null or empty!");
        //去除首尾空白字符
        this.expression = expression.trim();
        this.checker = checker != null ? checker :
                (item) -> item != null && item.length() > 13;
    }

    /**
     * 表达式解析操作
     * @throws ExpressionParseException 假如表达式不合法，则抛出该异常
     */
    public void parse(){
        List<String> list = new ArrayList<>();
        //判断是否是+ -开头，是特殊处理
        if (XLPStringUtil.startsWith(expression, "[\\-+]")){
            char pre = expression.charAt(0);
            expression = expression.substring(1).trim();
            if (pre == '-') {
                list.add("-1");
                list.add("*");
            }
        }

        String item = "";
        //标记左括号已存在的个数
        int leftBracket = 0;
        //记录上一个字符
        char preCharacter = '\0';
        //记录上一个非空字符
        char preNotBlankChar = ' ';

        int len = expression.length();
        for (int i = 0; i < len; i++) {
            char c = expression.charAt(i);
            switch (c){
                case '+':
                case '-':
                    if (preCharacter == 'e' || preCharacter == 'E' || preNotBlankChar == '('){
                        item += c;
                    } else if (preNotBlankChar != '.' && preNotBlankChar != ')'
                            && !Character.isDigit(preNotBlankChar)){
                        throw newExpressionEvaluatorException(expression);
                    } else {
                        if (!item.isEmpty()) {
                            list.add(item);
                            isBigInteger = checker.isBigInteger(item);
                        }
                        list.add(String.valueOf(c));
                        item = "";
                        isAddOrSubtract = true;
                    }
                    break;
                case '*':
                case '/':
                    if (preNotBlankChar != '.' && preNotBlankChar != ')'
                            && !Character.isDigit(preNotBlankChar)){
                        throw newExpressionEvaluatorException(expression);
                    } else {
                        if (!item.isEmpty()) {
                            list.add(item);
                            isBigInteger = checker.isBigInteger(item);
                        }
                        list.add(String.valueOf(c));
                        item = "";
                        isMultiplyOrDivide = true;
                    }
                    break;
                case '(':
                    if (leftBracket < 0){
                        throw newExpressionEvaluatorException(expression);
                    }
                    if (preNotBlankChar == ')'){
                        list.add("*");
                    }
                    if (!item.matches("^[\\-+]$") && !item.isEmpty()){
                        throw newExpressionEvaluatorException(expression);
                    }
                    if (item.equals("-")){
                        list.add("-1");
                        list.add("*");
                        item = "";
                    }
                    list.add(String.valueOf(c));
                    leftBracket++;
                    this.hasBracket = true;
                    break;
                case ')':
                    if (preNotBlankChar != '.' && preNotBlankChar != ')'
                            && !Character.isDigit(preNotBlankChar)){
                        throw newExpressionEvaluatorException(expression);
                    }
                    leftBracket--;
                    if (!item.isEmpty()){
                        list.add(item);
                        isBigInteger = checker.isBigInteger(item);
                    }
                    list.add(String.valueOf(c));
                    item = "";
                    break;
                case 'e':
                case 'E':
                    if (item.equals(".") || (preCharacter != '.' && !Character.isDigit(preCharacter))
                            || XLPStringUtil.containSubString(item, "[eE]")){
                        throw newExpressionEvaluatorException(expression);
                    }
                    item += c;
                    isDecimal = true;
                    hasE = true;
                    break;
                case ' ':
                case '\t':
                case '\r':
                    break;
                case '.':
                    if ((!item.isEmpty() && !Character.isDigit(preCharacter))
                            || item.contains(".")
                            || XLPStringUtil.containSubString(item, "[eE]")){
                        throw newExpressionEvaluatorException(expression);
                    }
                    item += c;
                    isDecimal = true;
                    break;
                default:
                    if (!Character.isDigit(c) || (!item.isEmpty() && isBlank(preCharacter))){
                        throw newExpressionEvaluatorException(expression);
                    }
                    item += c;
            }
            if (!isBlank(c)){
                preNotBlankChar = c;
            }
            preCharacter = c;
        }

        if (leftBracket != 0 || preNotBlankChar == '.'
                || preNotBlankChar == 'E' || preNotBlankChar == 'e') {
            throw newExpressionEvaluatorException(expression);
        }
        if (!item.isEmpty()){
            list.add(item);
            isBigInteger = checker.isBigInteger(item);
        }
        items = list.toArray(new String[0]);
    }

    /**
     * 判断是否是空字符
     * @param c
     * @return true:是，false：不是
     */
    private boolean isBlank(char c){
        return c == ' ' || c == '\t' || c == '\r';
    }

    private ExpressionParseException newExpressionEvaluatorException(String expression){
        return new ExpressionParseException(expression + "：该表达不合法");
    }

    /**
     * 获取表达式的所有项
     * @return
     */
    public String[] getExpressionItems(){
        return items;
    }

    /**
     * 获取表达式的复杂度
     * @return
     */
    public ExpressionComplexity getExpressionComplexity(){
        if (isDecimal && isAddOrSubtract && isMultiplyOrDivide){
            if(hasBracket) return ExpressionComplexity.DECIMAL_ALL;
            return ExpressionComplexity.DECIMAL_A_S_M_D;
        }
        if (isDecimal && isAddOrSubtract){
            if (hasBracket) return ExpressionComplexity.DECIMAL_A_S_B;
            return ExpressionComplexity.DECIMAL_A_S;
        }
        if (isDecimal && isMultiplyOrDivide){
            if (hasBracket) return ExpressionComplexity.DECIMAL_M_D_B;
            return ExpressionComplexity.DECIMAL_M_D;
        }
        if (isAddOrSubtract && isMultiplyOrDivide){
            if (hasBracket) return ExpressionComplexity.INT_ALL;
            return ExpressionComplexity.INT_A_S_M_D;
        }
        if (isAddOrSubtract){
            if (hasBracket) return ExpressionComplexity.INT_A_S_B;
            return ExpressionComplexity.INT_A_S;
        }
        if (hasBracket) return ExpressionComplexity.INT_M_D_B;
        return ExpressionComplexity.INT_M_D;
    }

    /**
     * 表达式中是否有括号
     * @return true：有，false：无
     */
    public boolean hasBracket(){
        return hasBracket;
    }

    /**
     * 表达式中是否是大整数运算
     * @return true：是，false：否
     */
    public boolean isBigInteger(){
        return isBigInteger;
    }

    /**
     * 表达式是否包含科学计算
     *
     * @return true：是，false：否
     */
    @Override
    public boolean hasE() {
        return hasE;
    }

    /**
     * 把数学表达式字符串转换成逆波兰表达式，并与数组格式返回
     *
     * @return
     */
    @Override
    public String[] toRPN() {
        String[] items = getExpressionItems();
        if (XLPArrayUtil.isEmpty(items)) return new String[0];

        List<String> result = new ArrayList<>(items.length);
        Stack<String> stack = new Stack<>();
        for (String s : items) {
            switch (s){
                case "+":
                case "-":
                    while (!stack.isEmpty() && !stack.peek().equals("(")){
                        // 弹出之前的所有操作符
                        result.add(stack.pop());
                    }
                    stack.push(s);
                    break;
                case "*":
                case "/":
                    while (!stack.isEmpty() && (stack.peek().equals("*")
                        || stack.peek().equals("/"))){
                        result.add(stack.pop());
                    }
                    stack.push(s);
                    break;
                case "(":
                    stack.push(s);
                    break;
                case ")":
                    while (!stack.isEmpty() && !stack.peek().equals("(")){
                        result.add(stack.pop());
                    }
                    // 弹出左括号
                    stack.pop();
                    break;
                default:
                    result.add(s);
            }
        }
        while (!stack.isEmpty()){
            result.add(stack.pop());
        }
        return result.toArray(new String[0]);
    }

    public static void main(String[] args) {
        ExpressionItemParser parser = new ExpressionItemParser("-1+2+3/(1+2*3.)-1-2 -(4+3)");
        parser.parse();

        System.out.println(Arrays.toString(parser.toRPN()));
    }
}
